<?
require_once('FactoryHelper.php');
require_once('ModelCollection.php');
require_once('ModelMapper.php');
require_once('ObjectWatcher.php');
require_once(dirname(__FILE__).'/../Helpers/ArrayUtils.php');

/**
 * Klasa bazowa DomainModel - Wzorzec DomainModel
 * @param integer @id
 * @param boolean @isGhost
 * @param User $user
 *
 */
abstract class DomainModel{
	protected $id;
	protected $isGhost;
	protected $user;
	protected $nextRecord;
	protected $previousRecord;
	protected $reloadParam = false;
	
	/**
	 * Metoda __call - mapuje wywolania nieznanych metod z prefixami set,get,has 
	 *
	 * @param string $name
	 * @param unknown_type $args
	 * @return unknown
	 */
	public function __call($name, $args) {
		$prefix = substr($name, 0, 3);
		if ($prefix == 'set' || $prefix == 'get' || $prefix == 'has') {
			$propertyName = strtolower(substr($name, 3, 1)).substr($name, 4);
			
			if (property_exists($this, $propertyName)) {
				if ($prefix == 'set') {
					if (count($args) == 1) {
						$this->{$propertyName} = $args[0];
						$this->markDirty();
					} else {
						die('<b style="color: red;">Function '.$name.' expects only one parameter</b><br />');
					}
					//	throw new Exception('Function '.$name.' expects only one parameter', 1);
				} else {
					if (count($args) == 0) 
						return $this->{$propertyName};
					else{
						die('<b style="color: red;">Function '.$name.' expects no parameters</b><br />');
					}
						//throw new Exception('Function '.$name.' expects no parameters', 1);
				}
			} else{
				die('<b style="color: red;">Property '.$propertyName.' does not exists</b><br />');
				//throw new Exception('Property '.$propertyName.' does not exists');
			}
		} else {
			die('<b style="color: red;">Method '.$propertyName.' does not exists</b><br />');
			//throw new Exception('Method \''.$name.'\' does not exists', 1);
		}
	}
	
	/**
	 * Konstruktor modelu
	 *
	 * Jesli nie podano id i nie ustawion isGhost na true konstruktor pobierze identyfikator dla nowego rekordu za pomoca metody mappera modelu newId();
	 * Zaznaczy utworzony obiekt metoda markNew() - Unit of Work - spowoduje to automatyczne utrwalenie obiektu destruktorem klasy ObjectWatcher
	 *  
	 * @param integer $id
	 * @param User $user
	 * @param boolean $isGhost
	 */
	function __construct($id = null, User $user = null, $isGhost = null){
		$this->id = $id;
		//debug($id);
		$this->isGhost = $isGhost;
		$this->user = $user;
		if (!$this->isGhost){	
			if (!$this->id){
				//debug($this->getFinder(get_class($this))->newId());
				//$this->id = $this->getFinder(get_class($this))->newId();
				$this->getFinder(get_class($this))->addToMap($this);
				$this->markNew();
			}
		}
	}
	
	/**
	 * Metoda getId - zwraca wartosc parametru $id
	 *
	 * @return integer $id
	 */
	function getId(){
		return $this->id;
	}
	
	/**
	 * Metoda getData - zwraca tablice informacji o zmiennych zdefiniowanych w dannym obiekcie klasy DomainModel
	 *
	 * @return array $vars
	 */
	public function getData() {
		$vars = get_object_vars($this);
		return $vars;
	}
	
	/**
	 * Metoda toArray - zwraca tablice assocjacyjna zmapowanych zmiennych zdefiniowanych w klasie DomainModel
	 *
	 * @return array $array
	 */
	public function toArray() {
		return $this->getData();
	}
	
	/**
	 * Metoda markNew - wykonuje metode statyczna klasy ObjectWatcher - dodajaca dany obiekt klasy DomainModel do tablicy 
	 * zawierajacej informacje o obiektach do utrwalenia w bazie przez destruktor klasy ObjectWatcher
	 *
	 */
	function checkIsNew(){
		return ObjectWatcher::checkIsNew($this);
	}
	
	/**
	 * Metoda markNew - wykonuje metode statyczna klasy ObjectWatcher - dodajaca dany obiekt klasy DomainModel do tablicy 
	 * zawierajacej informacje o obiektach do utrwalenia w bazie przez destruktor klasy ObjectWatcher
	 *
	 */
	function markNew(){
		ObjectWatcher::addNew($this);
	}
	/**
	 * Metoda markDirty - wykonuje metode statyczna klasy ObjectWatcher - dodajaca dany obiekt klasy DomainModel do tablicy
	 * zawierajacej informacje o obiektach, na ktorych dokonano modyfikacji - do utrwalenia w bazie przez destruktor klasy ObjectWatcher
	 *
	 */
	
	/**
	 * Metoda markDeleted - wykonuje metode statyczna klasy ObjectWatcher - dodajaca dany obiekt klasy DomainModel do tablicy 
	 * zawierajacej informacje o obiektach do uusuniecia z bazy przez destruktor klasy ObjectWatcher
	 *
	 */
	function markDeleted(){
		ObjectWatcher::addDeleted($this);
	}
	
	function delete(){
		$this->markDeleted($this);
	}
	
	/**
	 * Metoda markDirty - wykonuje metode statyczna klasy ObjectWatcher - dodajaca dany obiekt klasy DomainModel do tablicy
	 * zawierajacej informacje o obiektach, na ktorych dokonano modyfikacji - do utrwalenia w bazie przez destruktor klasy ObjectWatcher
	 *
	 */
	
	function markDirty(){
		ObjectWatcher::addDirty($this);
	}
	
	/**
	 * Metoda markClean - wykonuje metode statyczna klasy ObjectWatcher - usuwa z tablicy obiektow tzw brudnych dany obiekt klasy DomainModel
	 * powoduje to wstrzymanie destruktora przed utrwaleniem w bazie danych wprowadzonych zmian w modelu
	 *
	 */
	function markClean(){
		ObjectWatcher::addClean($this);
	}
	
	/**
	 * Metoda statyczna getCollection zwraca obiekt klasy ModelCollection zawierajacy pusta kolekcje obiektow DomainModel
	 *
	 * @param string $type
	 * @return unknown
	 */
	static function getCollection($type){
		return FactoryHelper::getCollection($type, 'Collection');
	}
	
	/**
	 * Metoda collection
	 * Wywoluje metode getCollection na "sobie" - pobiera pusta kolekcje swojego modelu
	 *
	 */
	function collection(){
		return self::getCollection(get_class($this));
	}
	
	
	/**
	 * Metoda statyczna getFinder zwraca obiekt ModelMapper na podstawie przekazanego parametru
	 *
	 * @param string $type
	 * @return ModelMapper $mapper
	 */
	static function getFinder($type){
		return ModelMapper::instance($type);
	}
	
	function reload(){
		$this->reloadParam = true;
		$obj = $this->finder()->find($this->id);
		$obj->reloadParam = false;
		$obj->isGhost = false;
		return $obj;
	}
	/**
	 * Metoda finder - zwraca obiekt klasy ModelMapper dla siebie
	 *
	 * @return ModelMapper $mapper
	 */
	function finder(){
		return self::getFinder(get_class($this));
	}
}

?>